Obtainment methods for every Pokémon in every game - #5
Merged
Conversation
- pipeline: PokéAPI + PokemonDB + Bulbapedia merged into `public/obtain/{id}.json`
- UI: `HOW TO OBTAIN` section in `PokemonCard.tsx`, lazy-fetched per Pokémon
- full encounter detail Gen 1-7, location lines Gen 8-9, derived fallbacks
- 10 TDD tasks: `src/obtain/` parse/merge modules, build script, dataset, UI wiring
- parser code grounded against live PokemonDB/Bulbapedia/PokéAPI structures
- covers derived fallbacks, version folding, lazy `public/obtain/{id}.json` fetch
- `src/obtain/types.ts`: `ObtainFile`/`ObtainGame`/`ObtainEntry` schema - `VERSION_GROUP_VERSIONS`, `VERSION_TO_GROUP`, `GROUP_GEN` derived from `GENERATIONS` - `isObtainFile` guard for runtime JSON validation
- `encountersToEntries` groups `/pokemon/{id}/encounters` by version
- merges same location+method+condition slots: level span, summed `chance`
- rod tier and unmapped method slugs preserved as `conditions`
- `parseWhereToFind` reads `table.vitals-table` rows keyed by `igame` slug - location links become `wild` entries; `<small>` notes classify to `transfer`/`unavailable`/`evolve`/`egg`/`special` - adds `node-html-parser` devDependency (script-side only)
- `parseTradeLists` walks mw-headline sections mapped in `TRADE_SECTIONS` - give/receive species read in order from `(Pokémon)` title links per row - `bulbaNameToSlug` normalizes names to PokéAPI slugs with overrides
- generic regex drops U+0027 and U+2019 so curly-quote names slug correctly - consolidates duplicate-key `SLUG_OVERRIDES` apostrophe entries - regression tests for curly-apostrophe Farfetch'd/Sirfetch'd
- `assembleObtainFile` merges PokéAPI-first, PokemonDB fallback, trades appended - empty versions derive `evolve` → `egg` → `transfer` (default forms only) - identical versions fold into one `ObtainGame` row per version group
- `bun` resolves module aliases from `$cwd/tsconfig.json`, not the referenced `tsconfig.app.json` project - without it, `scripts/*.mts` importing `src/` modules that use `@/` fail to resolve under `bun`
- `scripts/build-obtain-data.mts`: PokéAPI + PokemonDB + Bulbapedia → `public/obtain/{id}.json`
- disk cache in `scripts/.cache/obtain/` (gitignored), per-host throttling, `--only` flag
- completeness gate fails the run if any default species file is missing
- lazy-fetches `obtain/{id}.json` under `BASE_URL` when enabled
- module cache + inflight dedupe, mirrors `useApiDetail`
- validates payload with `isObtainFile` guard
- generation groups collapsible, current gen expanded by default - method tags tinted via palette vars; level/rate/condition chips per entry - loading and `OBTAIN DATA UNAVAILABLE` states
- `public/obtain/{id}.json` for every species and variety
- output of `scripts/build-obtain-data.mts` against PokéAPI/PokemonDB/Bulbapedia
- `Section` gains optional `onToggle`; obtain data fetches on first open - section sits between EVOLUTION and MOVES, `defaultOpen` false - `count` shows number of game rows once loaded
- `prettyCondition` maps slug families: `time-`/`weather-`/`season-` strip, `slot2-` → `GBA:`, swarm/radar/radio toggles, den rarity/rating, coins - chip style matches method tags: solid border, `0.68rem`, letter-spacing - test fixture uses real dataset slugs (`time-night`, `weather-intense-sun`)
- skip derivation entirely when a version already has an `unavailable` entry - forbid the EGG fallback in `NO_BREEDING_GROUPS` (`red-blue`, `yellow`, `lets-go-pikachu-lets-go-eevee`, `legends-arceus`) — EVOLVE still allowed - `LIMITED_DEX_GROUPS` (LGPE, Legends: Arceus) emit `unavailable` / "Not in this game's Pokédex" instead of any derived row when a species has zero entries there - drop the Bulbapedia NPC trade append when the version's PokéAPI entries already include a `trade` method (richer npc-trade encounter slots) - update `obtainAssemble.test.ts` for the new gating: egg case now pins `gold-silver`, `red-blue` falls to `transfer`, plus new cases for unavailable-suppresses-derivation, limited-dex no-data, and trade dedupe
- drop derived EGG/EVOLVE rows that were stacking on top of `unavailable` entries (1,743 rows previously double-counted) - remove EGG fallback rows from gen-1 and limited-dex groups (`red-blue`, `yellow`, `lets-go-pikachu-lets-go-eevee`, `legends-arceus`) where breeding isn't actually available - replace derived rows with `unavailable` / "Not in this game's Pokédex" for species absent from the LGPE/Legends: Arceus regional dex, and drop duplicate NPC trade rows already covered by PokéAPI's `npc-trade` slots
- `useObtainData` now derives its returned `data` from `state.data.pokemonId
=== pokemonId`, falling back to the module cache — navigating with the
section closed no longer leaks the previous Pokémon's game count into
`PokemonCard`'s `HOW TO OBTAIN` count
- `ObtainMethods` treats `enabled && !data && !error` as loading too, so
there's no one-frame "OBTAIN DATA UNAVAILABLE" flash before the fetch
effect's first state update lands
- `<ObtainMethods key={pokemon.id} .../>` in `PokemonCard` resets expansion
state per Pokémon instead of carrying over the previous one's open gens
- default-expand the first gen present when `currentGen` isn't in the file
(regional forms like Alolan Vulpix report gen 1 but their obtain file
starts at gen 7) instead of expanding nothing
- `tsconfig.json` now also carries the `@/` mapping (bun script resolution) - `CLAUDE.md` said three files must stay in sync; it's four
- method tags and condition chips now pull colors from `TYPE_COLORS` (same palette/glow pattern as the type pills in `PokemonCard.tsx`) instead of the flat 4-tint set — the section reads far less green-and-grey - condition chips get a category icon (◔ time, ✿ season, ☂︎/☀︎/❄︎/⚡︎ weather, ≈ rods, ★ raid den, ⚑ story progress, ⇄ trade, ◎ GBA slot, ✧ other), rendered with the U+FE0E text-presentation selector so they never flip to colored emoji - rename the `special` method tag label to `OTHER` (display only — `special` stays the data value) - expand `prettyCondition`/`CONDITION_LABELS` with plain-English glosses for the overworld/wanderer/spot/ambush/berry-tree/honey-tree/headbutt/ safari-zone/mount-item/save-data condition families so no chip is left unexplained - add a compact, collapsed-by-default `LEGEND` disclosure above the gen list, glossing every method tag and condition icon; hides the native `<details>` marker so the custom ▶/▼ is the only disclosure indicator - add `VG_REGION` overrides for version groups whose home region differs from their generation's default (FireRed/LeafGreen, HeartGold/SoulSilver, Omega Ruby/Alpha Sapphire, Let's Go, BDSP, Legends: Arceus) — gen headers now only show a region when every game under them shares it, and each `GameRow` labels its own region when it's an outlier (fixes BDSP/Legends: Arceus showing under "GEN VIII · GALAR", etc.)
- gen header shows all covered regions: `GEN VIII · GALAR / SINNOH / HISUI` - per-game region labels kept for remake/spin-off rows
- tabs are region names (`KANTO`…`PALDEA` + `HISUI`), no gen numbers - BDSP joins Sinnoh, Legends: Arceus gets its own Hisui tab, remakes go home - per-row region labels and their CSS dropped — the tab already says it
- `legends-arceus` groups under Sinnoh; `HISUI` tab removed - its rows read `LEGENDS ARCEUS · ANCIENT SINNOH`
- `HISUI · ANCIENT SINNOH` tab sits between Sinnoh and Unova - row-level ancient-Sinnoh suffix and its CSS dropped
- `.prettierignore` skips `public/obtain/` — minified JSON is intentional - unblocks `pre-push`'s `format:check` over 1,351 dataset files
✅ Deploy Preview for thepokemax ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
codebend3r
added a commit
that referenced
this pull request
Aug 22, 2026
- PR bodies drifted in shape across `#3`, `#5`, and `#6` — bullets in two, bold-lead paragraphs in the third, section names picked per PR - skill fixes the contract: lede, `## What changed`, `## Verification`, `## Notes`, with per-subsystem sections when the change spans several - bullets are the default form; prose is confined to the lede - title rules mirror the commit-subject style already in `git log` - `--body-file` is required, not preferred: backticks inside a double-quoted `--body` string are executed by the shell - carries `CLAUDE.md`'s zero-AI-attribution rule into PR titles and bodies
codebend3r
added a commit
that referenced
this pull request
Aug 22, 2026
- both skills previously banned prefixes outright, matching the 60 commits measured at the time — that rule is now reversed by convention - `PMX: ` is mandatory on every hand-written commit subject and PR title - form follows the sister repo's `CJR:` convention: code, colon, one space - pokemax capitalizes the first word after the prefix; `codebender-portfolio` lowercases it, so the two repos differ past the colon - version bumps take the prefix too — `PMX: 0.3.4` - `(#5)` squash suffixes still come from GitHub, never typed by hand - this subject is the first commit written under the new rule
codebend3r
added a commit
that referenced
this pull request
Aug 22, 2026
- PR bodies drifted in shape across `#3`, `#5`, and `#6` — bullets in two, bold-lead paragraphs in the third, section names picked per PR - skill fixes the contract: lede, `## What changed`, `## Verification`, `## Notes`, with per-subsystem sections when the change spans several - bullets are the default form; prose is confined to the lede - title rules mirror the commit-subject style already in `git log` - `--body-file` is required, not preferred: backticks inside a double-quoted `--body` string are executed by the shell - carries `CLAUDE.md`'s zero-AI-attribution rule into PR titles and bodies
codebend3r
added a commit
that referenced
this pull request
Aug 22, 2026
- both skills previously banned prefixes outright, matching the 60 commits measured at the time — that rule is now reversed by convention - `PMX: ` is mandatory on every hand-written commit subject and PR title - form follows the sister repo's `CJR:` convention: code, colon, one space - pokemax capitalizes the first word after the prefix; `codebender-portfolio` lowercases it, so the two repos differ past the colon - version bumps take the prefix too — `PMX: 0.3.4` - `(#5)` squash suffixes still come from GitHub, never typed by hand - this subject is the first commit written under the new rule
codebend3r
added a commit
that referenced
this pull request
Aug 22, 2026
* PMX: Drop always-true clause from `encountersToEntries` - `METHOD_MAP` has no entry mapping to `'special'` - so `method === 'special'` already implies `!METHOD_MAP[slug]` - second clause could never be false when the first was true * PMX: Make `isObtainFile` validate the shape its consumers index - derive `ObtainMethod` from a new `OBTAIN_METHODS` list — one vocabulary - guard now checks `games[].gen`/`versionGroup`/`versions`/`entries` - rejects entries whose `method` is not a known method - previously `ObtainMethods.tsx` indexed `METHOD_LABEL` on unvalidated strings - verified all 1351 files in `public/obtain/` still pass * PMX: Give the region model one owner in `generations.ts` - new `REGIONS` + `REGION_OF_VERSION_GROUP` map every version group to its setting - `GAMES_BY_REGION` now derives from `REGIONS` instead of restating it - `ObtainMethods.tsx` drops `VG_REGION`, `REGION_ORDER`, `REGION_LABELS` - kills the `getGen(gen).region` fallback, which was wrong for every remake and was the reason `VG_REGION` had to exist as a patch table - Hisui's note comes from `REGIONS`, not a one-entry label override - rename `crt-obtain-gen`/`crt-obtain-gen-toggle` — the rows are regions, not gens - new `regions.test.ts` pins coverage and ordering against `GENERATIONS` * PMX: Collapse the obtain loading contract into one status union - `useObtainData` returns `ObtainState` — `idle` / `loading` / `error` / `ready` - the enabled-but-not-yet-started frame now reports `loading` from the hook - drops the compensating `loading || (enabled && !data && !error)` in the view - drops the stale-id reconciliation — `settled.id` scopes failures to their id - `ObtainMethods` takes `state` + `currentGen`, down from five props - `enabled` no longer passed twice from `PokemonCard.tsx` - ready branch split into `ObtainRegions` so the data is non-null by type - loading/idle tests move to `useObtainData.test.ts` where the inputs live * PMX: Drop the nullable expansion state from `ObtainRegions` - `Set<string> | null` existed only because the default region was unknown until the file loaded — inside `ObtainRegions` it is known at mount - plain `useState<Set<string>>` with a lazy initializer replaces it - `toggle` no longer closes over a render-scoped `expanded` fallback - not routed through `useExpandedRegions` on purpose: its persisted value would carry one Pokémon's open region onto the next * PMX: Fold the condition tables into one `CONDITION_FAMILIES` model - `CONDITION_PREFIXES`, `conditionMeta`'s if-chain, and `LEGEND_CONDITIONS` all dispatched on the same slug families — a new family meant three edits - one table now carries prefix, icon, color, label strip, and legend gloss - `LEGEND_CONDITIONS` derives from it instead of restating icon/color by hand - `RODS` deleted — `ROD_METHODS` exported from `@/obtain/pokeapi` and shared by the build parser and the chip renderer - diffed old vs new over all 341 condition slugs in `public/obtain/` — identical - new test pins one chip per family, including the unknown-slug fallback * PMX: Move the obtain display vocabulary into `src/obtain/labels.ts` - `ObtainMethods.tsx` was 410 lines, ~250 of them static label tables - `METHOD_LABEL`, `METHOD_COLOR`, `CONDITION_*`, `LEGEND_*`, `levelRate`, `prettyCondition`, `conditionMeta` now live beside the domain they describe - component drops to 176 lines of rendering - `regionLabel` takes `RegionMeta` instead of an ad-hoc object shape - `LEGEND_CONDITIONS` uses `flatMap` so it narrows without `?? ''` padding - new `obtainLabels.test.ts` pins label/color/gloss coverage to `OBTAIN_METHODS` * PMX: Add a `create-pull-request` skill so PRs share one format - PR bodies drifted in shape across `#3`, `#5`, and `#6` — bullets in two, bold-lead paragraphs in the third, section names picked per PR - skill fixes the contract: lede, `## What changed`, `## Verification`, `## Notes`, with per-subsystem sections when the change spans several - bullets are the default form; prose is confined to the lede - title rules mirror the commit-subject style already in `git log` - `--body-file` is required, not preferred: backticks inside a double-quoted `--body` string are executed by the shell - carries `CLAUDE.md`'s zero-AI-attribution rule into PR titles and bodies * PMX: Point `CLAUDE.md` at the `create-pull-request` skill - line 13 claimed `pokemax-commit-format` governed "PR titles and bodies too" — two skills owning PR format is the drift the new skill removes - that bullet now scopes to commit messages; a new bullet hands PR titles and bodies to `create-pull-request` - both still carry the zero-AI-attribution rule, so narrowing one bullet does not open a gap * PMX: Add the missing `pokemax-commit-format` skill, repo-local - `CLAUDE.md` has mandated this skill since `e1a3761` but no file existed at the path it named, so the rule was unenforceable - rules derived by measuring the last 60 commits, not invented: 188 body bullets, 0 ending in a period, 0 prose lines, subject median 53 chars - lands in `.claude/skills/` beside `create-pull-request` rather than `~/.claude/skills/` — pokemax skills belong to the repo - `CLAUDE.md` path updated to match; the `Don't` section reference needed no change since it names the skill without a path * PMX: Cut duplicate CI runs and pin the toolchain in both workflows - `ci.yml` ran on `push: branches: ['**']` and `pull_request`, so every commit on a PR branch triggered two identical full runs - push now scopes to `main`; `pull_request` covers branch work - `bun-version: latest` made CI non-deterministic — a bun release could break a green tree with no repo change; both workflows pin `1.4.0`, matching the local toolchain and the `.nvmrc`/`engines` pinning style - `actions/cache@v4` on `~/.bun/install/cache`, keyed by `bun.lock` - `ci.yml` gains `permissions: contents: read`; it inherited the repo default and needs no write scope - `timeout-minutes` on all three jobs so a hung runner cannot burn minutes - `workflow_dispatch` added to `ci.yml` for manual re-runs - concurrency group keys on `github.workflow` too, so `ci.yml` and a future workflow on the same ref cannot cancel each other - deploy keeps its own `test` step on purpose — `workflow_dispatch` can ship a commit CI never gated * PMX: Require the `PMX:` repo-code prefix on commits and PRs - both skills previously banned prefixes outright, matching the 60 commits measured at the time — that rule is now reversed by convention - `PMX: ` is mandatory on every hand-written commit subject and PR title - form follows the sister repo's `CJR:` convention: code, colon, one space - pokemax capitalizes the first word after the prefix; `codebender-portfolio` lowercases it, so the two repos differ past the colon - version bumps take the prefix too — `PMX: 0.3.4` - `(#5)` squash suffixes still come from GitHub, never typed by hand - this subject is the first commit written under the new rule * PMX: Stop `.husky/pre-commit` failing docs-only commits - `oxfmt` exits 2 when no staged file is a type it formats, printing to stderr and leaving stdout empty - husky runs hooks under `set -e`, so the bare `unformatted=$(…)` assignment inherited that 2 and aborted the commit - any commit staging only `.md`, `.svg`, or `.png` was blocked - `|| true` on the assignment restores the intended behavior: no formattable files means nothing to reformat, so the hook exits 0 - latent since the hook was written; surfaced once `bun install` recreated `.husky/_` in this worktree and the hooks began running at all * PMX: Rename `pokemax-commit-format` to `commit-format` - the skill lives in the pokemax repo, so the repo name in the skill name was redundant — `.claude/skills/commit-format/` - frontmatter `name:` and the `# Commit format` heading follow the path - `CLAUDE.md` and `AGENTS.md` updated in both the Do and Don't sections - `AGENTS.md` pointed at `~/.Codex/skills/pokemax-commit-format/SKILL.md`, which never existed; it now names the repo-local file like `CLAUDE.md` - `docs/superpowers/plans/2026-07-31-obtainment-methods.md` still cites the old name and is left alone — it records what was true when it was written
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Every Pokémon's card gains a HOW TO OBTAIN section: per-game encounter locations with levels/rates/conditions, gifts, NPC trades, breeding info, and derived fallbacks (evolve / hatch / transfer) — grouped by region tabs (
KANTO…PALDEA, withHISUI · ANCIENT SINNOHunder Sinnoh).How
scripts/build-obtain-data.mtsmerges PokéAPI encounters (full Gen 1-7 detail), PokemonDB where-to-find tables (all games incl. Gen 8-9), and Bulbapedia in-game-trade lists intopublic/obtain/{id}.json(1,351 files, disk-cached + throttled scrape)src/obtain/(pokeapi,pokemondb,bulbapedia,assemble), unit-tested with real-structure fixturesuseObtainDatalazy-fetches a file only when the section first opens;ObtainMethodsrenders region tabs,TYPE_COLORS-tinted method tags, icon+label condition chips (no raw slugs), and a collapsible legendtsc -bcleanKnown limitations